/**
 * MIT License
 *
 * Copyright (C) 2023 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

import fs from '@ohos.file.fs';
import image from '@ohos.multimedia.image';
import util from '@ohos.util';
import { TurboModule } from '@rnoh/react-native-openharmony/ts';
import type { TurboModuleContext } from '@rnoh/react-native-openharmony/ts';
import Logger from './Logger';
import picker from '@ohos.multimedia.cameraPicker';
import camera from '@ohos.multimedia.camera'
import { BusinessError } from '@kit.BasicServicesKit';
import cameraPicker from '@ohos.multimedia.cameraPicker';
import { buffer } from '@kit.ArkTS';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { TM } from './generated/ts';
import { dataSharePredicates } from '@kit.ArkData';
import { abilityAccessCtrl,common, Permissions } from '@kit.AbilityKit';
import { bundleManager } from '@kit.AbilityKit';

export type MediaType = 'photo' | 'video' | 'mixed';
export type CameraType = 'back' | 'front';
type ImagePickerOptions = TM.ImagePicker.ImagePickerOptions
type ImagePickerResponse = TM.ImagePicker.ImagePickerResponse

const PHOTO_EXT_LIST =
  ['xbm', 'tif', 'pjp', 'svgz', 'jpg', 'jpeg', 'ico', 'tiff', 'gif', 'svg', 'jfif', 'webp', 'png', 'bmp', 'pjpeg',
    'avif']

const TAG = 'ImagePicker：';
const fetchColumns = [
  photoAccessHelper.PhotoKeys.URI,
  photoAccessHelper.PhotoKeys.PHOTO_TYPE,
  photoAccessHelper.PhotoKeys.DISPLAY_NAME,
  photoAccessHelper.PhotoKeys.SIZE,
  photoAccessHelper.PhotoKeys.DATE_ADDED,
  photoAccessHelper.PhotoKeys.DATE_MODIFIED,
  photoAccessHelper.PhotoKeys.DURATION,
  photoAccessHelper.PhotoKeys.WIDTH,
  photoAccessHelper.PhotoKeys.HEIGHT,
  photoAccessHelper.PhotoKeys.DATE_TAKEN,
  photoAccessHelper.PhotoKeys.ORIENTATION,
  photoAccessHelper.PhotoKeys.TITLE,
]

const mdType = ['photo', 'video', 'mixed']

function isPhoto(ext: string) {
  return PHOTO_EXT_LIST.includes(ext);
}

export class RNImagePickerTurboModule extends TurboModule implements TM.ImagePicker.Spec {

  constructor(protected ctx: TurboModuleContext) {
    super(ctx);

  }

  public showImagePicker(options: ImagePickerOptions, callback: (response: ImagePickerResponse) => void): void {
    let sheets = []
    if (options.customButtons && options.customButtons.length > 0) {
      sheets = options.customButtons.map((item) => {
        const res = { customButtons: item.name } as ESObject
        const sheetsItem = {
          title: item.title,
          action: () => {
            callback(res)
          }
        }
        return sheetsItem
      })
    }

    this.ctx.getUIContext()?.showActionSheet({
      title: options.title,
      message: '',
      autoCancel: true,
      confirm: {
        value: options.cancelButtonTitle,
        action: () => {
          const res = { didCancel: true } as ESObject
          callback(res)
        }
      },
      cancel: () => {
        const res = { didCancel: true } as ESObject
        callback(res)
      },
      alignment: 1,
      offset: { dx: 0, dy: -20 },
      sheets: [
        {
          title: options.takePhotoButtonTitle,
          action: () => {
            this.launchCamera(options, callback)
          }
        },
        {
          title: options.chooseFromLibraryButtonTitle,
          action: async () => {
            this.launchImageLibrary(options, callback)
          }
        },
        ...sheets
      ]
    })
  }

  public launchCamera(options: ImagePickerOptions, callback: (response: ImagePickerResponse) => void): void {

    let str = this.setMemoryPoints()
    this.checkPermissionGrant('ohos.permission.CAMERA').then(async (status) => {
      if (status === -1) {
        if (str === '0') {
          this.requestSystemAuth(['ohos.permission.CAMERA']).then(async (isAllowed) => {
            this.updateMemoryPoints()

            if (!isAllowed) {
              const res = { error: 'Permissions weren\'t granted' }  as ESObject
              callback(res)
              return
            }
            await this.launchCameraLogic(options, callback)

          })
        } else {
          // 去app设置相机权限页面
          this.openThePermissionDeniedDialog(options, callback)
        }

      } else {
        await this.launchCameraLogic(options, callback)
      }
    })
  }

  private async launchCameraLogic(options: ImagePickerOptions, callback: (response: ImagePickerResponse) => void) {
    try {
      let results = {} as ImagePickerResponse
      // 相机逻辑
      let pickerProfile: picker.PickerProfile = {
        cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK
      };
      if (options.cameraType === "front") {
        pickerProfile.cameraPosition = camera.CameraPosition.CAMERA_POSITION_FRONT;
      };

      let pickerResult: picker.PickerResult = await picker.pick(
        this.ctx.uiAbilityContext,
        this.getMediaTypeByOption(options.mediaType as MediaType),
        pickerProfile
      );
      if (pickerResult.resultCode == -1) {
        results.didCancel = true
        callback(results)
        return;
      };

      this.createDir(options)

      let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.ctx.uiAbilityContext);
      let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();

      predicates.equalTo('uri', pickerResult.resultUri);
      let fetchOption: photoAccessHelper.FetchOptions = {
        fetchColumns: fetchColumns,
        predicates: predicates
      };

      let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
        await phAccessHelper.getAssets(fetchOption);
      const asset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
      const { type } = this.getImgTypeAndName(asset.uri);
      results = await this.getImagePickerResponse(asset)
      if (asset.get(photoAccessHelper.PhotoKeys.ORIENTATION) !== 0) {
        results.isVertical = false
      }
      let file = fs.openSync(asset.uri, fs.OpenMode.CREATE);
      let userMediaType = options.mediaType
      if (!userMediaType || !mdType.includes(userMediaType)) {
        userMediaType = 'photo'
      }
      if (userMediaType !== 'video' && isPhoto(type)) {
        const { width, height, uri, imageType, fileSize } = await this.getImageInfo(options, asset, type)
        results.width = width;
        results.height = height;
        results.type = imageType
        results.uri = uri
        if (!uri.startsWith('file://')) {
          results.uri = 'file://' + uri;
        }
        if (fileSize !== 0) {
          results.fileSize = fileSize
        }
      } else {
        results.uri = this.copyFileToCache(file.fd, type, options);
      };
      if (!results.uri.startsWith('file:')) {
        results.uri = this.copyFileToCache(file.fd, type, options);
      }
      if (options.noData === true) {
        delete results.data
      }
      results.fileName = results.uri.split("/").pop()
      if (!results.type?.includes("image")) {
        results = {
          path: results.path,
          uri: results.uri
        } as ESObject
      }
      if (results.path?.startsWith("file://")) {
        results.path = results.path.slice(6)
      }
      fs.closeSync(file.fd)
      callback(results)
      Logger.info(`${TAG} the pick pickerResult is:` + JSON.stringify(asset));
    } catch (error) {
      Logger.error(`${TAG} the pick call failed. error code: ${error.code}`);
      const res= { error: error.message } as ESObject
      callback(res)
    }
  }

  public launchImageLibrary(options: ImagePickerOptions, callback: (response: ImagePickerResponse) => void): void {

    let str = this.setMemoryPoints()
    this.checkPermissionGrant('ohos.permission.CAMERA').then(async (status) => {
      if (status === -1) {
        if (str === '0') {
          this.requestSystemAuth(['ohos.permission.CAMERA']).then(async (isAllowed) => {
            this.updateMemoryPoints()

            if (!isAllowed) {
              const res = { error: 'Permissions weren\'t granted' }  as ESObject
              callback(res)
              return
            }
            await this.launchImageLibraryLogic(options, callback)

          })
        } else {
          this.openThePermissionDeniedDialog(options, callback)
        }

      } else {
        await this.launchImageLibraryLogic(options, callback)
      }
    })
  }

  private async launchImageLibraryLogic(options: ImagePickerOptions, callback: (response: ImagePickerResponse) => void) {
    let photoSelectResult: photoAccessHelper.PhotoSelectResult
    try {
      let userMediaType = options.mediaType
      if (!userMediaType || !mdType.includes(userMediaType)) {
        userMediaType = 'photo'
      }
      let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
      const Types = photoAccessHelper.PhotoViewMIMETypes;
      PhotoSelectOptions.MIMEType = userMediaType == 'photo' ? Types.IMAGE_TYPE :
        (userMediaType == 'video' ? Types.VIDEO_TYPE : Types.IMAGE_VIDEO_TYPE);
      PhotoSelectOptions.maxSelectNumber = 1;
      if (options.allowsEditing === false) {
        PhotoSelectOptions.isEditSupported = options.allowsEditing
      }
      let photoPicker = new photoAccessHelper.PhotoViewPicker();
      photoSelectResult = await photoPicker.select(PhotoSelectOptions)
      Logger.info(`${TAG} select info ${JSON.stringify(photoSelectResult)}`)
      if (photoSelectResult.photoUris.length === 0) {
        const results = { didCancel: true } as ESObject
        callback(results)
        return false
      }
    } catch (err) {
      Logger.error(`${TAG} PhotoViewPicker.select failed with err: ` + JSON.stringify(err));
      callback({ error: err.message } as ESObject)
      return false
    }

    this.createDir(options)

    let photoAsset: photoAccessHelper.PhotoAsset
    try {
      let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.ctx.uiAbilityContext);
      let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();

      // 配置查询条件，使用PhotoViewPicker选择图片返回的uri进行查询
      predicates.equalTo('uri', photoSelectResult.photoUris[0]);

      let fetchOption: photoAccessHelper.FetchOptions = {
        fetchColumns: fetchColumns,
        predicates: predicates
      };
      let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
        await phAccessHelper.getAssets(fetchOption);
      // 得到uri对应的PhotoAsset对象，读取文件的部分信息
      photoAsset = await fetchResult.getFirstObject();

    } catch (err) {
      Logger.error(`${TAG} getAssets failed, error: ${err.code}, ${err.message}`);
      callback({ error: err.message } as ESObject)
      return false
    }
    if (!photoAsset) return
    const { type } = this.getImgTypeAndName(photoAsset.uri);
    let results: ImagePickerResponse
    results = await this.getImagePickerResponse(photoAsset)
    let file = fs.openSync(photoAsset.uri, fs.OpenMode.CREATE);
    let userMediaType = options.mediaType
    if (!userMediaType || !mdType.includes(userMediaType)) {
      userMediaType = 'photo'
    }
    if ((userMediaType !== 'video') && isPhoto(type)) {
      if (photoAsset.get(photoAccessHelper.PhotoKeys.ORIENTATION) !== 0) {
        results.isVertical = false
      }

      const { width, height, uri, imageType, fileSize } = await this.getImageInfo(options, photoAsset, type, false)
      results.width = width;
      results.height = height;
      results.type = imageType
      results.uri = uri
      if (!uri.startsWith('file://')) {
        results.uri = 'file://' + uri;
      }
      if (fileSize !== 0) {
        results.fileSize = fileSize
      }
    } else {
      results.uri = this.copyFileToCache(file.fd, type, options, false);
    };
    if (!results.uri.startsWith('file:')) {
      results.uri = this.copyFileToCache(file.fd, type, options, false);
    }
    if (options.noData === true) {
      delete results.data
    }
    results.fileName = results.uri.split("/").pop()
    if (!results.type?.includes("image")) {
      results = {
        path: results.path,
        uri: results.uri
      } as ESObject
    }
    if (results.path?.startsWith("file://")) {
      results.path = results.path.slice(6)
    }
    fs.closeSync(file.fd)
    callback(results);
  }

  private async getImageInfo(options: ImagePickerOptions, photoAsset: photoAccessHelper.PhotoAsset, type: string, isLaunchCamera = true) {
    let files = fs.openSync(photoAsset.uri, fs.OpenMode.CREATE)
    let { maxWidth, maxHeight } = options;
    maxWidth = Math.abs(maxWidth)
    maxHeight = Math.abs(maxHeight)
    const imageSourceApi: image.ImageSource = image.createImageSource(files.fd);
    const pixelMap = await imageSourceApi.createPixelMap({editable: true})
    const imageInfo: image.ImageInfo = imageSourceApi.getImageInfoSync(0)
    let scaleNum = 1
    let imageWidth = imageInfo.size.width
    let imageHeight = imageInfo.size.height
    let imageType= imageInfo.mimeType
    if (maxWidth && imageWidth > maxWidth) {
      scaleNum = maxWidth / imageWidth
    }
    if (maxHeight && imageHeight > maxHeight) {
      scaleNum = maxHeight / imageHeight
    }
    Logger.info(`${TAG} ========== scaleNum ${JSON.stringify(scaleNum)}`)
    let uri
    if (options.storageOptions?.path) {
      uri = this.getStorageOptionsFilePath(type, options.storageOptions?.path)
    } else {
      uri = this.getCacheFilePath(type)
    }

    let width: number = imageInfo.size.width
    let height: number = imageInfo.size.height
    let fileSize = 0
    let quality = Math.abs(options.quality) * 100
    if (quality === 0 || quality > 100) {
      quality = 100
    }
    if (scaleNum !== 1 || quality !== 100) {
      await pixelMap.scale(scaleNum, scaleNum)
      const imagePackerApi: image.ImagePacker = image.createImagePacker();
      const file = fs.openSync(uri, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE)
      let packOpts: image.PackingOption = { format: imageInfo.mimeType, quality }
      const buffer: ArrayBuffer = await imagePackerApi.packToData(pixelMap, packOpts)
      await fs.write(file.fd, buffer)

      // 压缩写入后重新获取图片信息
      let scaleFiles = fs.openSync(uri, fs.OpenMode.CREATE)
      let stat = fs.statSync(scaleFiles.fd);
      fileSize = stat.size;
      const scaleImageSourceApi: image.ImageSource = image.createImageSource(scaleFiles.fd);
      const scaleImageInfo: image.ImageInfo = scaleImageSourceApi.getImageInfoSync(0)
      width = scaleImageInfo.size.width
      height = scaleImageInfo.size.height

      await imagePackerApi.release()
      await scaleImageSourceApi.release()

      fs.closeSync(scaleFiles.fd)
    } else {
      uri = this.copyFileToCache(files.fd, type, options, isLaunchCamera);
    }
    await imageSourceApi.release()
    await pixelMap.release()

    fs.closeSync(files.fd)

    return {
      uri,
      width,
      height,
      imageType,
      fileSize
    }
  }

  private copyFileToCache(fd: number, type: string, options: ImagePickerOptions, isLaunchCamera = true) {
    try {
      let filePath
      if (options.storageOptions?.path && isLaunchCamera) {
        filePath = this.getStorageOptionsFilePath(type, options.storageOptions?.path)
      } else {
        filePath = this.getCacheFilePath(type)
      }
      Logger.info(`${TAG} ========   ${filePath}`)

      fs.copyFileSync(fd, filePath, 0)

      return 'file://' + filePath
    } catch (e) {
      Logger.info(`${TAG} 复制到应用缓存区失败! ${JSON.stringify(e)}`);
    }
  }

  private createDir(options: ImagePickerOptions) {
    if(options.storageOptions && options.storageOptions?.path) {
      let dirPath = this.ctx.uiAbilityContext.cacheDir + `/${options.storageOptions?.path}`;
      const bool = fs.accessSync(dirPath)
      Logger.info(`${TAG} bool ${bool}`)
      if(!bool) {
        fs.mkdirSync(dirPath);
      }
    }
  }

  private getCacheFilePath(type) {
    return this.ctx.uiAbilityContext.cacheDir + '/rn_image_picker_lib_temp_' + util.generateRandomUUID(true) + '.' + type;
  }

  private getStorageOptionsFilePath(type: string, path: string) {
    return this.ctx.uiAbilityContext.cacheDir + '/' + path + '/rn_image_picker_lib_temp_' + util.generateRandomUUID(true) + '.' + type;
  }

  private async getImagePickerResponse(photoAsset: photoAccessHelper.PhotoAsset) {
    let res = {
      data: await this.getFileBase64(photoAsset.uri),
      fileName: photoAsset.displayName,
      isVertical: true,
      width: photoAsset.get(photoAccessHelper.PhotoKeys.WIDTH),
      height: photoAsset.get(photoAccessHelper.PhotoKeys.HEIGHT),
      fileSize: photoAsset.get(photoAccessHelper.PhotoKeys.SIZE),
      path: photoAsset.uri,
      origURL: photoAsset.uri,
      originalRotation: photoAsset.get(photoAccessHelper.PhotoKeys.ORIENTATION),
      timestamp: this.convertTimestampToCustomISO8601(Number(photoAsset.get(photoAccessHelper.PhotoKeys.DATE_MODIFIED)))
    } as ImagePickerResponse

    return res
  }

  private async getFileBase64(uri: string) {
    let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
    let arrayBuffer = new ArrayBuffer(100 * 1024 * 1024);
    let readLen = fs.readSync(file.fd, arrayBuffer);
    return buffer.from(arrayBuffer, 0, readLen).toString('base64');
  }

  private getImgTypeAndName(imgUri: string) {
    let res = { type: '', fileName: '' };
    const mimeUri = imgUri.substring(0, 4)
    if (mimeUri === 'file') {
      let i = imgUri.lastIndexOf('/')
      res.fileName = imgUri.substring(i + 1)
      i = imgUri.lastIndexOf('.')
      res.type = 'Unknown'
      if (i != -1) {
        res.type = imgUri.substring(i + 1)
      }
    }
    return res;
  }

  private openThePermissionDeniedDialog(options: ImagePickerOptions, callback: (response: ImagePickerResponse) => void) {
    Logger.info(`${TAG} 打开弹窗，选择去app设置相机权限页面`)
    this.ctx.getUIContext()?.showAlertDialog(
      {
        title: options.permissionDenied?.title,
        message: options.permissionDenied?.text,
        autoCancel: false,
        alignment: 3,
        offset: { dx: 0, dy: -20 },
        gridCount: 3,
        primaryButton: {
          value: options.permissionDenied?.okTitle,
          action: () => {
            Logger.info(`${TAG} 按钮 okTitle`)
            callback({ didCancel: true } as ESObject)
          }
        },
        secondaryButton: {
          enabled: true,
          defaultFocus: true,
          style: 1,
          value: options.permissionDenied?.reTryTitle,
          action: async () => {
            Logger.info(`${TAG} 按钮 reTryTitle `)
            // 去往App的设置权限页面
            let context = this.ctx.uiAbilityContext as common.UIAbilityContext;
            let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT;
            try {
              const data: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleFlags)
              Logger.info(`${TAG} BundleInfo ${JSON.stringify(data)}`)
              Logger.info(`${TAG} context.startAbility 设置页面`)
              context.startAbility({
                bundleName: 'com.huawei.hmos.settings',
                abilityName: 'com.huawei.hmos.settings.MainAbility',
                uri: 'application_info_entry',
                parameters: {
                  pushParams: data.name
                }
              });
            } catch (err) {
              let message = (err as BusinessError).message;
              Logger.error('testTag', 'getBundleInfoForSelf failed: %{public}s', message);
            }
          }
        },
        cancel: () => {
          const res = { error: 'Permissions weren\'t granted' }  as ESObject
          callback(res)
          return
        }
      }
    )
  }

  private updateMemoryPoints() {
    let pathDir = this.ctx.uiAbilityContext.filesDir
    let filePath = pathDir + "/IMAGE_PICKER_TEST.txt";
    let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    let str: string = "1";
    fs.writeSync(file.fd, str);
    fs.closeSync(file);
  }

  private setMemoryPoints() {
    let pathDir = this.ctx.uiAbilityContext.filesDir
    let filePath = pathDir + "/IMAGE_PICKER_TEST.txt";
    let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    let buf = new ArrayBuffer(4096);
    return fs.readSync(file.fd, buf).toString();
  }

  private getMediaTypeByOption(mediaType: MediaType): cameraPicker.PickerMediaType[] {
    let mediaTypeArr: cameraPicker.PickerMediaType[] = [];
    if (mediaType === 'photo') {
      mediaTypeArr.push(picker.PickerMediaType.PHOTO)
    }
    if (mediaType === 'video') {
      mediaTypeArr.push(picker.PickerMediaType.VIDEO)
    }
    if (mediaType === 'mixed') {
      mediaTypeArr = [picker.PickerMediaType.PHOTO, picker.PickerMediaType.VIDEO]
    }
    return mediaTypeArr;
  }

  private convertTimestampToCustomISO8601(timestamp: number): string {
    Logger.info(`${TAG} timestamp ${timestamp}`)
    const date = new Date(timestamp * 1000);
    let isoString = date.toISOString();
    isoString = isoString.slice(0, -5) + 'Z';
    Logger.info(`${TAG} isoString ${isoString}`)

    return isoString;
  }

  private async checkPermissionGrant(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;

    // 获取应用程序的accessTokenID
    let tokenId: number = 0;
    try {
      let bundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
      let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
      tokenId = appInfo.accessTokenId;

      Logger.info(`${TAG} tokenId ${tokenId}`)
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      Logger.error(`${TAG} Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);
    }

    // 校验应用是否被授予权限
    try {
      grantStatus = await atManager.checkAccessToken(tokenId, permission);
      Logger.info(`${TAG} grantStatus ${grantStatus}`)
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      Logger.error(`${TAG} Failed to check access token. Code is ${err.code}, message is ${err.message}`);
    }

    return grantStatus;
  }

  private async requestSystemAuth(permissionArr: Permissions[]): Promise<boolean> {
    // 权限管理
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    let context = this.ctx.uiAbilityContext as common.UIAbilityContext;

    // let permissionArr: Permissions[] = ['ohos.permission.CAMERA']
    try {
      // 请求用户授权
      const data = await atManager.requestPermissionsFromUser(context, permissionArr);
      const grantStatus: number[] = data.authResults;

      // 检查所有权限是否都被授予
      for (let i = 0; i < grantStatus.length; i++) {
        if (grantStatus[i] !== 0) {
          // 如果有任何一个权限被拒绝，返回 false
          return false;
        }
      }

      // 所有权限都被授予
      return true;
    } catch (err: any) {
      // 错误处理
      Logger.error(`${TAG} Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
      return false;
    }
  }
}
